package com.atguigu.gmall.item.service.Impl;

import com.atguigu.gmall.common.RedisConst;
import com.atguigu.gmall.common.result.Result;

import com.atguigu.gmall.common.util.JSONs;
import com.atguigu.gmall.feign.product.ProductFeignClient;
import com.atguigu.gmall.starter.cache.aop.annotation.Cache;
import com.atguigu.gmall.item.service.SkuDetailService;
import com.atguigu.gmall.model.product.BaseCategoryView;
import com.atguigu.gmall.model.product.SkuInfo;
import com.atguigu.gmall.model.product.SpuSaleAttr;
import com.atguigu.gmall.model.to.SkuDetailTo;
import com.atguigu.gmall.starter.cache.CacheService;
import com.fasterxml.jackson.core.type.TypeReference;

import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class SkuDetailServiceImpl implements SkuDetailService {

    @Autowired
    ProductFeignClient productFeignClient;
    @Autowired
    ThreadPoolExecutor corePool;
    @Autowired
    CacheService cacheService;
    @Autowired
    RBloomFilter<Object> skuIdBloom;

    @Autowired
    StringRedisTemplate redisTemplate;
    @Autowired
    RedissonClient redissonClient;
    @Cache(cacheKey= RedisConst.SKU_CACHE_KEY_PREFIX+"#{#args[0]}",
            bloomName = "skuIdBloom",bloomValue="#{#args[0]}")
    @Override
    public SkuDetailTo getSkuItemDetail(Long skuId){
        log.info("正在从数据库等确定商品详情:{}",skuId);
        return getSkuItemDetailFromDb(skuId);
    }
    /**
     * 查询商品详情,使用Redisson提供的分布式锁
     * @param skuId
     * @return
     */
    //@Override
    public SkuDetailTo getSkuItemDetailWithRedisLock(Long skuId) {
            String cacheKey=RedisConst.SKU_CACHE_KEY_PREFIX+ skuId;
            //1.查询缓存
        SkuDetailTo cacheData = cacheService.getCacheData(cacheKey,
                new TypeReference<SkuDetailTo>() {
        });
        if(cacheData==null){
            log.info("SkuDetail:缓存未命中,准备回源",skuId);
            //缓存没有准备回源
            if(skuIdBloom.contains(skuId)){
                log.info("SkuDetail:布隆过滤器中有,放过请求并回源",skuId);
                //开始查库,加锁防止缓存击穿
                //获得锁
                RLock lock = redissonClient.getLock(RedisConst.SKUDETAIL_LOCK_PREFIX + skuId);
                //加锁
                boolean tryLock = false;
                try {
                     tryLock = lock.tryLock();
                    //加锁成功
                    if (tryLock){
                        log.info("SkuDetail:回源锁加锁成功",skuId);

                        SkuDetailTo db = getSkuItemDetailFromDb(skuId);
                        //查到后保存到缓存[防止null值穿透,防雪崩]
                        cacheService.saveCategory(cacheKey,db);

                        return db;
                    }
                } finally {
                    try {
                        //解锁
                        lock.unlock();
                    }catch (Exception e){
                        log.error("SkuDetail:回源锁解锁失败",e);
                    }

                }
                //加锁失败
                log.info("SkuDetail:加锁失败,1s后查看缓存",skuId);

                try {
                    Thread.sleep(1000);
                }catch (Exception e){
                   log.error("SkuDetail:睡眠异常",e);
                }
            }
            //布隆过滤器中没有,拦截请求
            log.info("布隆过滤器没有,打回请求",skuId);
            return null;
        }
        log.info("SkuDetail:缓存命中",skuId);
        //缓存不为空
        return cacheData;
    }

    /**
     * 引入缓存的查询商品详情
     * @param skuId
     * @return
     */

    public SkuDetailTo getSkuItemDetailWithRedis(Long skuId) {
        String cacheKey = RedisConst.SKU_CACHE_KEY_PREFIX+skuId;
        //查询缓存
        SkuDetailTo cacheData = cacheService.getCacheData(cacheKey,
                new TypeReference<SkuDetailTo>() {});
        //判断
        if(cacheData==null){
            //缓存中没有,查库
            //回源之前,先问下布隆,有没有
            if(skuIdBloom.contains(skuId)){
                //布隆过滤器中有
                log.info("SkuDetail{0}缓存未命中,正在回源",skuId);
                //注意加锁,否则可能会被击穿: 最好加分布式锁
                String token = UUID.randomUUID().toString();
                //去redis中占坑
                SkuDetailTo skuItemDetailFromDb= null;
                Boolean lock = redisTemplate.opsForValue()
                        .setIfAbsent(RedisConst.LOCK_PREFIX+skuId,
                                "token",10,TimeUnit.SECONDS);
                if (lock){
                    //即使业务断电,redis也会自动删,自动过期
                    //非原子操作
                        redisTemplate.expire("lock", 10, TimeUnit.SECONDS);
                    try {
                        //就算百万请求,也只有一个true
                        log.info("SkuItemDetail:{}真查库:"+skuId);
                        skuItemDetailFromDb = getSkuItemDetailFromDb(skuId);
                        cacheService.saveCategory(cacheKey,skuItemDetailFromDb);
                        //业务超长,要自动续期

                        //业务期间,如果发生了断电风险,会导致finally不执行,解锁失败
                    }finally {
                        //释放锁
                        //String lock1 = redisTemplate.opsForValue().get("lock");
                        //删锁脚本(原子性操作)->(对比并删除一步操作)
                        String deleteScripte = "if redis.call('get',KEYS[1]) == ARGV[1]  then return redis.call('del',KEYS[1]) else  return 0 end";
                        Long result = redisTemplate.execute(new DefaultRedisScript<>(deleteScripte,Long.class),
                                Arrays.asList("RedisConst.LOCK_PREFIX+skuId"), token);
                        if (result==1){
                            log.info("分布式锁解锁完成");
                        }else {
                            //别人的锁[说明之前由于业务卡顿,续期失败,锁自动释放,被别人抢到]
                            log.info("这是别人的锁,我不能释放");
                        }

                    }

                }else {
                    //抢锁失败
                    log.info("分布式锁抢锁失败,1s后直接查缓存");
                    try {
                        Thread.sleep(1000);
                        //100w请求,就等1s
                        cacheData=cacheService.getCacheData(cacheKey,
                                new TypeReference<SkuDetailTo>() {
                                });
                        return cacheData;
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }

                return skuItemDetailFromDb;

            }
            //布隆说没有
            log.info("SkuDetail{0}缓存未命中,bloom防火墙拦截住了",skuId);
            return null;

        }
        log.info("SkuDetail缓存命中");
        //缓存中有直接返回
        return cacheData;
    }

    /**
     * 回源查库
     * @param skuId
     * @return
     */
    //@Override
    public SkuDetailTo getSkuItemDetailFromDb(Long skuId) {
        SkuDetailTo skuDetailTo = new SkuDetailTo();



        //1.当前sku所在的分类完整信息s
            //三级分类详情
        CompletableFuture<Void> categoryViewfuture = CompletableFuture.runAsync(() -> {
            Result<BaseCategoryView> skuCategoryViewResult = productFeignClient.getSkuCategoryView(skuId);
            if (skuCategoryViewResult.isOk()) {
                skuDetailTo.setCategoryView(skuCategoryViewResult.getData());
            }
        }, corePool);


        //2. 当前sku信息
        //2.1 sku图片列表
        CompletableFuture<Void> skuInfofuture = CompletableFuture.runAsync(() -> {
            Result<SkuInfo> skuInfoResult = productFeignClient.getSkuInfo(skuId);
            if (skuInfoResult.isOk()) {
                skuDetailTo.setSkuInfo(skuInfoResult.getData());
            }
        }, corePool);


        //3.sku的价格
        CompletableFuture<Void> skuPricefuture = CompletableFuture.runAsync(() -> {
            Result<BigDecimal> skuPrice = productFeignClient.getSkuPrice(skuId);
            if (skuPrice.isOk()) {
                skuDetailTo.setPrice(skuPrice.getData());
            }
        }, corePool);


        //4.sku的spu所有销售属性集合
        CompletableFuture<Void> skuSaleAttrAndValuefuture = CompletableFuture.runAsync(() -> {
            Result<List<SpuSaleAttr>> skuSaleAttrAndValue = productFeignClient.getSkuSaleAttrAndValue(skuId);
            if (skuSaleAttrAndValue.isOk()) {
                skuDetailTo.setSpuSaleAttrList(skuSaleAttrAndValue.getData());
            }
        }, corePool);


        //sku下的所有可用销售属性组合valueJson
        CompletableFuture<Void> skuValueJsonfuture = CompletableFuture.runAsync(() -> {
            Result<Map<String, String>> skuValueJson = productFeignClient.getSkuValueJson(skuId);
            if (skuValueJson.isOk()) {
                skuDetailTo.setValuesSkuJson(JSONs.toStr(skuValueJson.getData()));
            }
        }, corePool);


        CompletableFuture.allOf
                (categoryViewfuture,skuInfofuture,skuPricefuture
                        ,skuSaleAttrAndValuefuture,skuValueJsonfuture).join();
        //allof返回的CompletableFuture总任务结束再往下
        return skuDetailTo;
    }


}
